﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using UBCon.Core.Enums;
using UBCon.Ribbon.Interfaces;

namespace UBCon.Ribbon
{
    public class ApplicationMenuItem : ListBoxItem, ICommandSource, IRibbonControl
    {
        #region Constructors
        static ApplicationMenuItem() 
        {
            Type ownerType = typeof(ApplicationMenuItem);
            ApplicationMenuItem.DefaultStyleKeyProperty.OverrideMetadata
                (
                    ownerType, 
                    new FrameworkPropertyMetadata(ownerType)
                );
        }

        public ApplicationMenuItem() : base() 
        {
            Binding binding = new Binding(".") { RelativeSource = new RelativeSource() { Mode = RelativeSourceMode.FindAncestor, AncestorType = typeof(ApplicationMenu) } };

            BindingOperations.SetBinding
                (
                    this,
                    ApplicationMenuItem.ParentMenuProperty,
                    binding
                );
        }
     
        #endregion

        #region Properties

        public ApplicationMenu ParentMenu
        {
            get { return (ApplicationMenu)GetValue(ParentMenuProperty); }
            set { SetValue(ParentMenuProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ParentMenu.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ParentMenuProperty =
            DependencyProperty.Register("ParentMenu", typeof(ApplicationMenu), typeof(ApplicationMenu), new PropertyMetadata((ApplicationMenu)null));

        public RibbonControlSize Size
        {
            get { return (RibbonControlSize)GetValue(SizeProperty); }
            set { SetValue(SizeProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Size.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SizeProperty = RibbonButton.SizeProperty.AddOwner(typeof(ApplicationMenuItem));

        public ImageSource Image
        {
            get { return (ImageSource)GetValue(ImageProperty); }
            set { SetValue(ImageProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Image.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ImageProperty = RibbonButton.ImageProperty.AddOwner(typeof(ApplicationMenuItem));
        /// <summary>
        /// Gets or sets content for title. This is a dependency property.
        /// </summary>
        public object TitleContent
        {
            get { return (object)GetValue(TitleContentProperty); }
            set { SetValue(TitleContentProperty, value); }
        }

        // Using a DependencyProperty as the backing store for TitleContent.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty TitleContentProperty =
            DependencyProperty.Register("TitleContent", typeof(object), typeof(ApplicationMenuItem), new PropertyMetadata((object)null));

        [Bindable(true),
        ReadOnly(false),
        Browsable(true)]
        public bool IsSelectable
        {
            get { return (bool)GetValue(IsSelectableProperty); }
            set { SetValue(IsSelectableProperty, value); }
        }

        // Using a DependencyProperty as the backing store for IsSelectable.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsSelectableProperty =
            DependencyProperty.Register("IsSelectable", typeof(bool), typeof(ApplicationMenuItem), new FrameworkPropertyMetadata(true));


        /// <summary>
        ///Gets or sets Command property of button. This is a dependency property.
        /// </summary>
        [Bindable(true),
        Browsable(false),
        ReadOnly(false)]
        public ICommand Command
        {
            get { return (System.Windows.Input.ICommand)GetValue(CommandProperty); }
            set { SetValue(CommandProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Command.  This enables animation, styling, binding, etc...

        public static readonly DependencyProperty CommandProperty =
            DependencyProperty.Register("Command", typeof(ICommand), typeof(ApplicationMenuItem), 
            new FrameworkPropertyMetadata((ICommand)null, new PropertyChangedCallback(ApplicationMenuItem.OnCommandPropertyChanged)));

        /// <summary>
        /// Gets or sets command target of button. This is a dependency property.
        /// </summary>
        [Bindable(true), 
        Category("Action")]
        public IInputElement CommandTarget
        {
            get { return (IInputElement)GetValue(CommandTargetProperty); }
            set { SetValue(CommandTargetProperty, value); }
        }

        // Using a DependencyProperty as the backing store for CommandTarget.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty CommandTargetProperty =
            DependencyProperty.Register("CommandTarget", typeof(IInputElement), typeof(ApplicationMenuItem), 
            new FrameworkPropertyMetadata((IInputElement)null));

        /// <summary>
        /// Gets or sets command parameter of button. This is a dependency property.
        /// </summary>
        [Bindable(true), 
         Category("Action")]
        [Localizability(LocalizationCategory.NeverLocalize)]
        public object CommandParameter
        {
            get { return (object)GetValue(CommandParameterProperty); }
            set { SetValue(CommandParameterProperty, value); }
        }

        // Using a DependencyProperty as the backing store for CommandParameter.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty CommandParameterProperty =
            DependencyProperty.Register("CommandParameter", typeof(object), typeof(ApplicationMenuItem), 
            new PropertyMetadata((object)null));

        public bool IsPressed
        {
            get { return (bool)GetValue(IsPressedProperty); }
            private set { SetValue(IsPressedProperty, value); }
        }

        // Using a DependencyProperty as the backing store for IsPressed.  This enables animation, styling, binding, etc...
        internal static readonly DependencyPropertyKey IsPressedPropertyKey =
            DependencyProperty.RegisterReadOnly("IsPressed", typeof(bool), typeof(ApplicationMenuItem), new FrameworkPropertyMetadata(false));
        internal static readonly DependencyProperty IsPressedProperty = ApplicationMenuItem.IsPressedPropertyKey.DependencyProperty;

        #endregion

        #region Depenency Property Changed Callbacks

        /// <summary>
        /// Invokes whn command property changes.
        /// </summary>
        /// <param name="o"></param>
        /// <param name="e"></param>
        private static void OnCommandPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            ApplicationMenuItem item = sender as ApplicationMenuItem;
            item.SetValue(ApplicationMenuItem.CommandTargetProperty, item);
            item.HookUpCommand(e.OldValue as ICommand, e.NewValue as ICommand);
        }

        #endregion

        #region Overrides

        protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
        {
            if (Command != null)
            {
                RoutedCommand commad = Command as System.Windows.Input.RoutedCommand;

                //If routed command
                if (commad != null)
                {
                    commad.Execute(CommandParameter, CommandTarget);
                }
                else
                {
                    (Command as ICommand).Execute(CommandParameter);
                }
            }

            if (!IsSelectable) 
            {
                e.Handled = true;
                
                //IsPressed had been true before user clicked
                Debug.Assert(!IsPressed);
                
                if (!IsPressed)
                {
                    SetIsPressedValue(true);
                }

                return;
            }
            base.OnMouseLeftButtonDown(e);
        }

        protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
        {
            if (IsPressed) 
            {
                SetIsPressedValue(false);

                if (ParentMenu != null) 
                {
                    ParentMenu.IsApplicationMenuOpen = false;
                }
            }
            base.OnMouseLeftButtonUp(e);
        }

        protected override void OnMouseLeave(MouseEventArgs e)
        {
            base.OnMouseLeave(e);
            
            if (e.LeftButton == MouseButtonState.Pressed)
            {
                if (IsPressed) 
                {
                    SetIsPressedValue(false);
                }
            }
        }

        #endregion

        #region Private Utilities

        private void SetIsPressedValue(bool value) 
        {
            SetValue(ApplicationMenuItem.IsPressedPropertyKey, value);
        }
        private void HookUpCommand(System.Windows.Input.ICommand oldCommand, System.Windows.Input.ICommand newCommand)
        {
            //First we need to remove old handler.
            if (oldCommand != null)
            {
                RemoveCommand(oldCommand, newCommand);
            }

            AddCommand(oldCommand, newCommand);
        }

        /// <summary>
        /// Removes old value of Command .
        /// </summary>
        /// <param name="oldCommand"></param>
        /// <param name="newCommand"></param>
        private void RemoveCommand(ICommand oldCommand, ICommand newCommand)
        {
            System.EventHandler handler = new EventHandler(CanExecuteChanged);
            oldCommand.CanExecuteChanged -= handler;
        }

        /// <summary>
        /// Setting up a handler for the new command.
        /// </summary>
        /// <param name="oldCommand"></param>
        /// <param name="newCommand"></param>
        private void AddCommand(ICommand oldCommand, ICommand newCommand)
        {
            EventHandler handler = new EventHandler(CanExecuteChanged);
            canExecuteChangedHandler = handler;
            if (newCommand != null)
            {
                newCommand.CanExecuteChanged += canExecuteChangedHandler;
            }
        }

        private void CanExecuteChanged(object sender, EventArgs e)
        {
            if (this.Command != null)
            {
                RoutedCommand command = Command as RoutedCommand;
                
                //If Routed command
                if (command != null)
                {
                    if (command.CanExecute(CommandParameter, CommandTarget))
                    {
                        IsEnabled = true;
                    }
                    else
                    {
                        IsEnabled = false;
                    }
                }

                // If not a routed command
                else
                {
                    if (Command.CanExecute(this.CommandParameter))
                    {
                        IsEnabled = true;
                    }
                    else
                    {
                        IsEnabled = false;
                    }
                }
            }
        }
        #endregion

        #region Event Handlers
        #endregion

        #region Fields
        
        private static System.EventHandler canExecuteChangedHandler;

        #endregion

        #region Events
        #endregion

        #region IRibbonControl Implementation

        /// <summary>
        /// Gets a boolean value indicating whether the contol's size is large
        /// </summary>
        public bool IsLarge
        {
            get { return true; }
        }

        #endregion
    }
}
